home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 002 / mcprint.arc / MC.C next >
Encoding:
C/C++ Source or Header  |  1987-07-23  |  23.5 KB  |  848 lines

  1. /*
  2.  *          m c . c
  3.  *
  4.  * Multi-column filter
  5.  *
  6.  */
  7.  
  8. #ifdef   DOCUMENTATION
  9.  
  10. title   mc   Convert One or More Files To Multi-column Format
  11. index      Convert a file to multi-column format
  12. index      Combine files in multi-column format
  13.  
  14. Synopsis
  15.  
  16.    mc [-t] [-c columns] [-h height] [-g gutter] [-w width]
  17.       [filespec | aliasspec]...
  18.  
  19. Description
  20.  
  21.    mc reads one or more files and converts them to a single multi-column
  22.    file that it writes to the standard output.
  23.  
  24.    Each line of each input file will occupy one row-column position in
  25.    the output file.  If an item is too wide for its column, it is
  26.    truncated with no message.  The items from any one file are placed in
  27.    order going down a column, and then across columns.  Thus, consider a
  28.    simple case first:
  29.  
  30.    .tp 11
  31.       mc a -c 2 -h 10
  32.  
  33.    produces a file whose first page looks like this:
  34.  
  35.       a1      a11
  36.       a2      a12
  37.       :      :
  38.       :      :
  39.       a10      a20
  40.  
  41.    ("-c 2" requests two columns; "-h 10" requests ten lines per page).
  42.  
  43. File Specifications
  44.  
  45.    The mc command line may contain more than one file specification;
  46.    however, the specifications may not be wild-carded.
  47.  
  48.    When there are multiple files, each column will come from a single
  49.    file:
  50.  
  51.    .tp 9
  52.       mc a b -c 2 -h 10
  53.  
  54.    produces:
  55.  
  56.       a1   b1
  57.       a2   b2
  58.       :   :
  59.       :   :
  60.       a10   b10
  61.  
  62.    Columns are filled by consecutive files, in rotating order:
  63.  
  64.    .tp 9
  65.       mc a b -c 4 -h 10
  66.  
  67.    produces:
  68.  
  69.       a1   b1   a11   b11
  70.       a2   b2   a12   b12
  71.       :   :   :   :
  72.       :   :   :   :
  73.       a10   b10   a20   b20
  74.  
  75.    If there is more than one file, and the number of columns does not
  76.    evenly divide the number of files, successive pages will be different.
  77.  
  78.    If a file reaches EOF while there is still data to be read from other
  79.    files, the ended file's columns will be blank from that point on.
  80.  
  81.    The use of "-" as a file specification causes the standard input to
  82.    be read.  If mc is invoked with no file arguments at all, it reads
  83.    the standard input file once.
  84.  
  85. Alias Specifications
  86.  
  87.    Alias specifications provide a method for controlling the placement
  88.    of file data.  An alias specification is a reference to another file
  89.    (or alias) specification.  File and alias specifications are numbered,
  90.    starting at one for the left-most such specification; switches and
  91.    their arguments do not affect the numbering.  The alias specification
  92.    #n indicates that the n'th specification is to be repeated.  Such a
  93.    specification is legal only if it refers to an earlier specification;
  94.    i.e. #n is only legal as the n+1'st, n+2'nd, etc. specification.
  95.    Thus:
  96.  
  97.    .tp 9
  98.       mc a #1 b #3 -c 4 -h 10
  99.  
  100.    produces:
  101.  
  102.       a1   a11   b1   b11
  103.       a2   a12   b2   b12
  104.       :   :   :   :
  105.       :   :   :   :
  106.       a10   a20   b10   b20
  107.  
  108.    This should be compared with:
  109.  
  110.       mc a a b b -c 4 -h 10
  111.  
  112.    which opens each of a and b twice and reads the copies in parallel,
  113.    placing two copies of each item on the page.
  114.  
  115. Last-page Handling
  116.  
  117.    mc will attempt to make the columns on the last page of output as
  118.    close in length as possible, rather than simply filling some columns
  119.    all the way to the bottom and leaving others empty.  This special
  120.    handling is enabled only when mc is given no more than one file
  121.    specification.
  122.  
  123. Switches
  124.  
  125.    The following switches are available:
  126.    .lm +8;
  127.  
  128.  -c   Next argument is the number of columns
  129.  -h   Next argument is the height, in lines, of a page
  130.  -g   Next argument is the gutter width (the space between columns)
  131.  -w   Next argument is the width, in characters, of a page
  132.  -t   Terminal mode; sets default height to 23, default width to 80, and, if
  133.    stdout is your terminal, pauses after each page
  134.  -d   Debug (conditionally-compiled code)
  135.  
  136.    .lm -8;
  137.  
  138. Defaults
  139.  
  140.    Height 58, width 132, no terminal mode; note that -t alters all three.
  141.    Gutter 1, max(number-of-file-and-alias-specs,2) columns.
  142.  
  143. Control Character Handling
  144.  
  145.    mc is designed to operate on text files, not binary files.  It sets
  146.    columns up based on how they will look when displayed.  Hence, it
  147.    processes control characters (anything that isprint() returns FALSE
  148.    to) as follows:
  149.  
  150.       <TAB> ("\t") is expanded into the equivalent number of spaces.
  151.       mc always assumes that there are tab stops every 8 character
  152.       positions.
  153.  
  154.       <BS> ("\b") subtracts one from the current cursor position.
  155.       Any printable characters received when the current cursor
  156.       position is over a previously read character is ignored -
  157.       i.e. overstruck combinations retain only the first character
  158.       read.  However, if the character being overstruck is a space
  159.       or an underscore ("_"), the overstriking character replaces
  160.       the current character.
  161.  
  162.       <BS> when the current cursor position is over the first
  163.       character in the line is ignored.
  164.  
  165.       <CR> ("\r") resets the current cursor position to the first
  166.       character of the line.
  167.  
  168.       <LF> ("\n") ends the current input item.
  169.  
  170.       All other control characters are discarded.
  171.  
  172. File Limits
  173.  
  174.    Different systems impose different limits on the number of files
  175.    mc may simultaneously have open.
  176.  
  177.    On RT-based systems, this limit is totally dynamic; opening too many
  178.    files is most likely to cause an error due to insufficient memory,
  179.    rather than a file system error per se.
  180.  
  181.    On RSX-based systems, the absolute limit is set at task-build time.
  182.    The distributed source, when built with BUILD, will allow for 10
  183.    open files.  Note that this total includes at least one file for
  184.    stdin and stdout (two if either is redirected).  Of course, it is
  185.    possible to run into memory limitations even under this limit.
  186.  
  187.    Aliases do not count against this limit, since they refer to
  188.    already-opened files.  Similarly, a "-" argument, implying stdin,
  189.    does not count, as it is simply a reference to the already-open
  190.    standard input.  If you are right at the limit, be sure to use
  191.    the standard input as one of your files; you are paying for it
  192.    to be open whether you use it or not.
  193.  
  194.    mc itself imposes another limit, which does include aliases.  In the
  195.    distributed code, this limit is set to a total of 20 file and alias
  196.    arguments.
  197.  
  198. Other Limits
  199.  
  200.    No column can contain more than 256 characters (compile-time
  201.    constant).
  202.  
  203. Diagnostics
  204.  
  205.    Insufficient memory - sorry
  206.  
  207.    Too many file and alias arguments
  208.  
  209.    .tp 2
  210.    Unreasonable -c/-g/-w combination
  211.       -- for example, c > w
  212.  
  213.    .tp 2
  214.    <value>: Bad <what> specification
  215.       -- Invalid value for something like -h
  216.  
  217.    <filespec>: Can't open: <why>
  218.  
  219. Suggested Improvements
  220.  
  221.    Anyone interested in improving this program might want to consider
  222.    the following suggestions.  Be warned that they are not as easy to
  223.    implement as they look!
  224.  
  225.    Make the last-page cleanup algorithm work for the multiple-files case.
  226.  
  227.    Add the ability to fold long items to the next entry for this file
  228.    (probably indented) rather than just chopping them off.  (This only
  229.    gets hard when you consider both overstrike handling and multiple
  230.    files!)
  231.  
  232.    Allow the automatic printing of the file name above the appropriate
  233.    columns at the top of each page.
  234.  
  235.    The techniques used are wasteful of space; in particular, the
  236.    gutters should not be taking up space in the data array!
  237.  
  238. Bugs
  239.  
  240. Author
  241.  
  242.    Original author unknown; extensively modified by Jerry Leichter
  243.  
  244. #endif
  245.  
  246. char    *documentation[] = {
  247.    "   mc [-t] [-c columns] [-h height] [-g gutter] [-w width]",
  248.    "      [filespec | aliasspec]...",
  249.    "",
  250.    "mc reads one or more files and converts them to a single multi-column file",
  251.    "that it writes to the standard output.   ",
  252.    "",
  253.    "The mc command line may contain more than one file specification; however,",
  254.    "the specifications may not be wild-carded.",
  255.    "",
  256.    "Each line of each input file will occupy one row-column position in the output"
  257.        ,
  258.    "file.  If an item is too wide for its column, it is truncated with no message."
  259.        ,
  260.    "",
  261.    "The items from any one file are placed in order going down a column, and then",
  262.    "across columns.  Columns are filled by consecutive files, in rotating order.",
  263.    "",
  264.    "The use of \"-\" as a file specification causes the standard input to be read."
  265.        ,
  266.    "If mc is invoked with no file arguments at all, it reads the standard input",
  267.    "file once.",
  268.    "",
  269.    "Alias specifications provide a method for controlling the placement of file",
  270.    "data.  An alias specification is a reference to another file (or alias)",
  271.    "specification.  File and alias specifications are numbered, starting at one",
  272.    "for the left-most such specification; switches and their arguments do not",
  273.    "affect the numbering.  The alias specification #n indicates that the n'th",
  274.    "specification is to be repeated.  Such a specification is legal only if it",
  275.    "refers to an earlier specification; i.e. #n is only legal as the n+1'st,",
  276.    "n+2'nd, etc. specification.",
  277.    "",
  278.    "mc will attempt to make the columns on the last page of output as close in",
  279.    "length as possible, rather than simply filling some columns all the way to",
  280.    "the bottom and leaving others empty.  This special handling is enabled only",
  281.    "when mc is given no more than one file specification.",
  282.    "",
  283.    "The following switches are available:",
  284.    "   ",
  285.    " -c   Next argument is the number of columns",
  286.    " -h   Next argument is the height, in lines, of a page",
  287.    " -g   Next argument is the gutter width (the space between columns)",
  288.    " -w   Next argument is the width, in characters, of a page",
  289.    " -t   Terminal mode; sets default height to 23, default width to 80,",
  290.    "   and, if stdout is your terminal, pauses after each page",
  291.    "",
  292.    "The default values are:",
  293.    "",
  294.    "   Height 58, width 132, no terminal mode; note that -t alters all three.",
  295.    "   Gutter 1, max(number-of-file-and-alias-specs,2) columns.",
  296.    0 };
  297.  
  298. #include <ctype.h>
  299. #include <stdio.h>
  300. #include <malloc.h>
  301. #include <string.h>
  302. #include <io.h>
  303.  
  304. #define exits(x)        exit(x)
  305. #define IO_ERROR        2
  306. #define FALSE           0
  307. #define TRUE            1
  308. #define EOS             0
  309.  
  310. /*
  311.  * Turn on to include debugging code
  312.  */
  313.  
  314. #define LINEMAX 256                     /* Maximum line length handled  */
  315. /*  (also maximum column width) */
  316. #define NFILES  20                      /* Maximum files (including     */
  317. /*  aliased files)              */
  318. #define ALIAS   '#'                     /* Marks an alias argument      */
  319. /*  (Can't be "-")              */
  320. int     debug   = 0;
  321. int     columns = -1;                   /* All these will be given      */
  322. int     gutter  = -1;                   /*  default values later unless */
  323. int     height  = -1;                   /*  the user sets them first    */
  324. int     width   = -1;                   /*  (to a positive value!)      */
  325.  
  326. int     pause   = FALSE;                /* Pause-at-end of page flag    */
  327. int     first   = TRUE;                 /* First-time-through flag      */
  328. int     cwidth;                         /* Total (column+gutter) width  */
  329. int     pagesize;                       /* Total bytes in page          */
  330. int     nf;                             /* Number of file & alias specs */
  331. int     files   = 0;                    /* Number of files still open   */
  332. int     aliases = 0;                    /* Number of alias specs        */
  333.  
  334. FILE    *file[NFILES];                  /* File pointers for our files  */
  335. char    line[LINEMAX];                  /* Input line buffer            */
  336. int     linelen;                        /* Length of a line in line[]   */
  337. int     lineend;                        /* Last usable line[] position  */
  338. char    *page;                          /* -> page paste-up matrix      */
  339. char    *copy();
  340.  
  341. main(argc,argv)
  342. int argc;
  343. char *argv[];
  344. {       
  345.    register char *p;
  346.    register int c,i;
  347.    int n;
  348.    FILE *fp;
  349.  
  350.    if (argc == 2 && argv[1][0] == '?' && strlen(argv[1]) == 1)
  351.    {       
  352.       help();
  353.       return;
  354.    }
  355.  
  356.    nf = argc - 1;
  357.    for (i = 1; i < argc; i++) {
  358.       p = argv[i];
  359.       if (*p == '-') {
  360.          if (p[1] == '\0')       /* stdin as a file      */
  361.             continue;       /* skip this one        */
  362.  
  363.          argv[i] = 0;
  364.          --nf;
  365.          for (++p; c = *p++;)
  366.             switch(tolower(c))
  367.             {
  368.             case 'd':
  369.                debug++;
  370.                break;
  371.             case 'c':
  372.                if (++i >= argc)
  373.                   usage();
  374.                columns = atoi(argv[i]);
  375.                if (columns <= 0)
  376.                   bad(argv[i],"columns");
  377.                argv[i] = 0;
  378.                --nf;
  379.                break;
  380.  
  381.             case 'g':
  382.                if (++i >= argc)
  383.                   usage();
  384.                gutter = atoi(argv[i]);
  385.                if (gutter <= 0)
  386.                   bad(argv[i],"gutter");
  387.                argv[i] = 0;
  388.                --nf;
  389.                break;
  390.  
  391.             case 'h':
  392.                if (++i >= argc)
  393.                   usage();
  394.                height = atoi(argv[i]);
  395.                if (height <= 0)
  396.                   bad(argv[i],"height");
  397.                argv[i] = 0;
  398.                --nf;
  399.                break;
  400.  
  401.             case 't':
  402.                if (height<0)
  403.                   height = 23;
  404.                if (width<0)
  405.                   width = 80;
  406.                if (isatty(fileno(stdout)))
  407.                   pause++;
  408.                break;
  409.  
  410.             case 'w':
  411.                if (++i >= argc)
  412.                   usage();
  413.                width = atoi(argv[i]);
  414.                if (width <= 0)
  415.                   bad(argv[i],"width");
  416.                argv[i] = 0;
  417.                --nf;
  418.                break;
  419.  
  420.             default:
  421.                usage();
  422.                break;
  423.             }
  424.       }
  425.    }
  426.  
  427.    if (nf > NFILES)
  428.       error("Too many file and alias arguments");
  429.  
  430.    if (nf == 0)
  431.    {       
  432.       nf = 1;                         /* Run as a filter      */
  433.       file[files++] = stdin;
  434.    }
  435.    else
  436.       for (i = 1; i < argc; i++)
  437.          if (p = argv[i])
  438.             switch (*p)
  439.             {
  440.             case '-':       /* stdin as a file      */
  441.                file[files++] = stdin;
  442.                break;
  443.  
  444.             case ALIAS:
  445.                n = atoi(&p[1]) - 1;
  446.                if (n < 0 || n >= files)
  447.                   error(
  448.                   "\"%s\": bad alias specification - no such file\n",p
  449.                       );
  450.                file[files++] = file[n];
  451.                aliases++;
  452.                break;
  453.  
  454.             default:
  455.                if ((fp = fopen(p,"r")) == NULL)
  456.                {       
  457.                   fprintf(stderr,
  458.                   "%s: ",p);
  459.                   perror("Can't open");
  460.                   exits(IO_ERROR);
  461.                }
  462.                file[files++] = fp;
  463.                break;
  464.             }
  465.  
  466.    files -= aliases;                       /* Aliases aren't open  */
  467.  
  468.    /*
  469.        * Establish defaults for any parameters the user didn't set
  470.        */
  471.    if (width < 0)
  472.       width = 132;
  473.    if (gutter < 0)
  474.       gutter = 1;
  475.    if (height < 0)
  476.       height = 58;
  477.    if (columns < 0)
  478.       if (nf > 1)
  479.          columns = nf;
  480.    else
  481.        columns = 2;
  482.  
  483.    /*
  484.     * The last column isn't followed by a gutter, but dealing with this makes
  485.     * the computation too complex; so we simply pretend the page is wider, which
  486.     * is ok since the code trims the trailing spaces that would go there anyway.
  487.     * This is, of course, quite wasteful of space, but then so is the whole algo-
  488.     * rithm; we shouldn't be storing ANY of the gutters explicitly.
  489.     */
  490.    width += gutter;
  491.    cwidth = width/columns;
  492.  
  493.    if (cwidth <= gutter || (cwidth - gutter) > LINEMAX)
  494.       error("Unreasonable -c/-g/-w combination\n");
  495.  
  496.    lineend = (int)line + (cwidth - gutter);
  497.  
  498.    page = malloc(pagesize = height * width);
  499.    if (page == NULL)
  500.       error("Insufficient memory - sorry\n");
  501.  
  502.    if (debug)
  503.    {       
  504.       fprintf(stderr, "width %d, height %d, columns %d, cwidth %d\n", width, height, columns, cwidth);
  505.       fprintf(stderr, "\tgutter %d, pause %d, pagesize %d, page at 0%o\n", gutter, pause, pagesize, page);
  506.       fprintf(stderr,"%d files(%d real + %d aliases)\n", nf, files, aliases);
  507.    }
  508.  
  509.    process();
  510.    free(page);
  511. }
  512.  
  513. /*
  514.  * Process all the data
  515.  */
  516. process()
  517. {       
  518.    register int    offset;                 /* Offset into page     */
  519.    register int    items;                  /* Counts items added   */
  520.    register int    maxitems;               /* Room for this many   */
  521.    int curfile;                            /* Current file         */
  522.  
  523.    maxitems = columns * height;
  524.    blank();
  525.    curfile = items = offset = 0;
  526.    while (get(file[curfile]))
  527.    {       
  528.       if (items >= maxitems)
  529.       {       
  530.          output(items);
  531.          blank();
  532.          items = offset = 0;
  533.       }
  534.       if (debug)
  535.          fprintf(stderr,"Inserting %s at offset %d, file %d\n", line, offset, curfile);
  536.       copy(page+offset,line,linelen);
  537.       items++;
  538.       if ((items % height) == 0)      /* Bottom of a column   */
  539.       {       
  540.          curfile = (curfile + 1) % nf;
  541.          if (debug)
  542.             fprintf(stderr,"Switching to file %d of %d\n", curfile,files);
  543.       }
  544.       offset += cwidth;
  545.    }
  546.    output(items);
  547. }
  548.  
  549. /*
  550.  * Print out the buffered page, which has been filled with items items.
  551.  */
  552. output(items)
  553. int items;                              /* # of items the caller used   */
  554. {       
  555.    int nrows;
  556.    register int i,col,row;
  557.  
  558.    if (debug)
  559.       fprintf(stderr,"output(%d)\n",items);
  560.  
  561.    if (items <= 0)                 /* Nothin' to do                */
  562.       return;
  563.  
  564.    /*
  565.        * Get number of rows we'll need.  This is the basis of the "last page"
  566.        * optimization - we don't use all the rows, just enough to hold everything
  567.        * (items/columns, rounded up).  If there's more than are one file, just use
  568.        * the whole page.
  569.        */
  570.    if (nf == 1)
  571.       nrows = (items + (columns - 1)) / columns;
  572.    else
  573.        nrows = height;
  574.  
  575.    if (debug)
  576.    {
  577.       fprintf(stderr,"items %d, nrows %d\n",items,nrows);
  578.       page[pagesize] = 0;
  579.       fprintf(stderr,"Dump of page:\n%s\n",page);
  580.    }
  581.  
  582.    if (first)
  583.       first = FALSE;
  584.    else
  585.    {       
  586.       if (pause)
  587.       {       
  588.          printf("\t\t\t      Type CTRL/Z to exit, any other key to continue...");
  589.          fflush(stdout);
  590.          i = kbhit();
  591.          putchar('\n');
  592.          if (i == 26)            /* CTRL/Z               */
  593.             exit();
  594.       }
  595.       putchar('\f');
  596.    }
  597.  
  598.    /*
  599.        * Scan through page[] row-wise, after having filled it column-wise.  (Page[]
  600.        * is laid out column-wise in memory.)
  601.        */
  602.    for (row = 0; row < nrows; row++)
  603.    {       
  604.       for (col = 0; col < columns; col++)
  605.          putitem(page+(row+col*nrows)*cwidth,
  606.          (col == columns - 1));
  607.       putchar('\n');
  608.    }
  609. }
  610.  
  611. /*
  612.  * Put out one item, possibly trimming trailing spaces
  613.  */
  614. putitem(base,trim)
  615. register char *base;                            /* First char to put    */
  616. int trim;                                       /* Trim trailing spaces */
  617. {       
  618.    register char *end;                     /* End of item          */
  619.  
  620.    end = &base[cwidth - 1];
  621.    if (trim)
  622.       while (*end == ' ')
  623.          --end;
  624.  
  625.    while (base <= end)
  626.       putchar(*base++);
  627. }
  628.  
  629. /*
  630.  * Blank out page[]
  631.  */
  632. blank()
  633. {       
  634.    register int n;
  635.  
  636.    for (n = 0; n < pagesize;)
  637.       page[n++] = ' ';
  638. }
  639.  
  640. /*
  641.  * Fill line[]; return FALSE when all files have reached EOF, TRUE until then.
  642.  */
  643. get(fp)
  644. FILE *fp;
  645. {       
  646.    register char *p;                       /* Current char pos     */
  647.    register char *high;                    /* Char pos high water  */
  648.    register int c;                         /* Character            */
  649.  
  650.    if (feof(fp))
  651.    {       
  652.       linelen = 0;                    /* Pretend we read ""   */
  653.       return(TRUE);
  654.    }
  655.  
  656.    high = p = line;
  657.    while ((c = getc(fp)) != EOF && c != '\n')
  658.       switch(c)
  659.       {
  660.       case '\b':
  661.          if (p > line)
  662.             --p;
  663.          break;
  664.  
  665.       case '\r':
  666.          p = line;
  667.          break;
  668.  
  669.       case '\t':
  670.          if (((p - line) & 07) != 07)
  671.             ungetc(c,fp);
  672.          c = ' ';
  673.          /*
  674.           * Fall through...
  675.           */
  676.       default:
  677.          if (isprint(c))
  678.          {       
  679.             if (((int)p < lineend) && (p == high || *p == '_' || *p == ' '))
  680.             {       
  681.                *p = c;
  682.                if (p == high)
  683.                   high++;
  684.             }
  685.             p++;
  686.          }
  687.          break;
  688.       }
  689.  
  690.    linelen = high - line;
  691.  
  692.    if (c != EOF)
  693.       return(TRUE);
  694.    else
  695.        return((--files != 0));
  696. }
  697.  
  698. bad(v,s)
  699. char *v;
  700. char *s;
  701. {       
  702.    error("\"%s\": bad %s specification\n",v,s);    
  703. }
  704.  
  705. usage()
  706. {
  707.    fprintf(stderr,"Usage:\n mc [-t] [-c columns] [-g gutter] ");
  708.    fprintf(stderr,"[-h height] [-w width] [file | #n]...\n");
  709.    error("mc ? for help");
  710. }
  711.  
  712. help()
  713. /*
  714.  * Give good help
  715.  */
  716. {       
  717.    register char   **dp;
  718.  
  719.    for (dp = documentation; *dp; dp++)
  720.       printf("%s\n",*dp);
  721. }
  722.  
  723. /*
  724.  *            c o p y . c
  725.  */
  726.  
  727. #ifdef    DOCUMENTATION
  728.  
  729. title    copy    Copy a Given Number of Bytes
  730. index        Copy a given number of bytes
  731.  
  732. synopsis
  733.     .s.nf
  734.     char *
  735.     copy(out, in, nbytes)
  736.     char        *out;    /* Output vector    */
  737.     char        *in;    /* Input vector        */
  738.     unsigned int    count;    /* Bytes to copy    */
  739.     .s.f
  740. Description
  741.  
  742.     Copy the indicated number of bytes from the input area
  743.     to the output area.  Return a pointer to the first free
  744.     byte in the output area.  (I.e., &out[count]).
  745.  
  746.     The copying will be faster if out and in are either both
  747.     even or both odd addresses.
  748.  
  749. #endif
  750.  
  751. #define   SHIFT   1
  752. #define   LOWBIT   01
  753.  
  754. char *copy(out, in, count)
  755. register char *out;
  756. register char *in;
  757. register unsigned int count;
  758. /*
  759.  * Copy a given number of bytes
  760.  */
  761. {
  762.    if (count != 0)
  763.    {
  764. #ifdef SHIFT
  765.       if (count > 10)
  766.       {
  767.       /*
  768.        * Try to optimize
  769.        */
  770.          if ((((unsigned int) in) & LOWBIT) != 0)
  771.          {
  772.             *out++ = *in++;
  773.             count--;
  774.          }
  775.          if ((((unsigned int) out) & LOWBIT) == 0)
  776.          {
  777.             count >>= SHIFT;   /* Get a word count      */
  778.             do
  779.             {
  780.                *((int *)out)++ = *((int *)in)++;
  781.             } while (--count != 0);
  782.             goto exit;
  783.          }
  784.       }   
  785. #endif
  786.       /*
  787.        * Here for small copies, strange machines, and copies where
  788.        * the output buffer isn't the same parity as the input buffer.
  789.        */
  790.       do
  791.       {
  792.          *out++ = *in++;
  793.       } while (--count != 0);
  794.    }
  795. exit:   return (out);
  796. }
  797.  
  798. /*
  799.  *            e r r o r . c
  800.  */
  801.  
  802. #ifdef    DOCUMENTATION
  803.  
  804. title    error    Fatal Error Exit
  805. index        Fatal error exit
  806.  
  807. synopsis
  808.     .s.nf
  809.     _error()
  810.  
  811.     error(format)
  812.     char        *format;
  813.     .s.f
  814. documentation
  815.  
  816.     Fatal error exits.  _error() halts, error() prints something
  817.     on stderr and then halts.
  818.  
  819. bugs
  820.  
  821. #endif
  822.  
  823. #include <stdarg.h>
  824.  
  825. char        recycle        = 0;    /* Prevents looping    */
  826.  
  827. error(format)
  828. char        *format;
  829. /*
  830.  * Error message before retiring.
  831.  */
  832. {
  833.    va_list args;
  834.  
  835.    va_start(args, format);
  836.     if (recycle++ == 0) {
  837.        vfprintf(stderr, format, args);
  838.     }
  839.    va_end(args);
  840.     _error();
  841. }
  842.  
  843. _error()
  844. {
  845.     abort();
  846. }
  847.  
  848.